package com.hmy.finance.service.receiveAdjust.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.ccp.starter.web.utils.UserContextUtil;
import com.hmy.finance.api.bo.core.request.ReceivableAdjustGenerateRequest;
import com.hmy.finance.api.bo.core.request.enums.ProfitLossTypeEnum;
import com.hmy.finance.api.bo.core.request.enums.ReceivableAdjustTypeEnum;
import com.hmy.finance.api.bo.core.request.vos.ReceivableAdjustDetailVo;
import com.hmy.finance.api.bo.core.response.BaseGenerateProfitLossResponse;
import com.hmy.finance.bo.receiveAdjust.request.QueryReceiveAdjustRequest;
import com.hmy.finance.bo.receiveAdjust.request.SaveReceiveAdjustRequest;
import com.hmy.finance.bo.receiveAdjust.request.SyncReceiveAdjustRequest;
import com.hmy.finance.bo.receiveAdjust.response.QueryAdjustRelatedContractResponse;
import com.hmy.finance.bo.receiveAdjust.response.QueryReceiveAdjustResponse;
import com.hmy.finance.bo.receiveAdjust.response.SaveReceiveAdjustResponse;
import com.hmy.finance.common.constants.DictionaryConstant;
import com.hmy.finance.common.enums.ReceiveAdjustEnum;
import com.hmy.finance.dal.mapper.ReceivableInventoryMapper;
import com.hmy.finance.dal.mapper.receiveAdjust.ReceiveAdjustDetailMapper;
import com.hmy.finance.dal.mapper.receiveAdjust.ReceiveAdjustMapper;
import com.hmy.finance.dal.po.ReceivableInventoryPo;
import com.hmy.finance.dal.po.receiveAdjust.ReceiveAdjustDetailPo;
import com.hmy.finance.dal.po.receiveAdjust.ReceiveAdjustPo;
import com.hmy.finance.manager.uua.DataDictionaryClientRpc;
import com.hmy.finance.service.core.IFinancialCoreDataService;
import com.hmy.finance.service.receiveAdjust.ReceiveAdjustDetailService;
import com.hmy.finance.service.receiveAdjust.ReceiveAdjustService;
import com.hmy.generator.common.enums.BusinessTypeEnum;
import com.hmy.generator.service.CommonNoGenerator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author wenjian
 * @description 表receive_adjust(应收调整主表)的数据库操作Service实现
 * @createDate 2024-02-26 10:16
 */
@Service
public class ReceiveAdjustServiceImpl extends ServiceImpl<ReceiveAdjustMapper, ReceiveAdjustPo> implements ReceiveAdjustService {

    private static final Logger LOG = LoggerFactory.getLogger(ReceiveAdjustServiceImpl.class);

    @Resource
    private CommonNoGenerator generator;

    @Resource
    private ReceivableInventoryMapper receivableInventory;

    @Resource
    private ReceiveAdjustMapper receiveAdjustMapper;

    @Resource
    private DataDictionaryClientRpc dataDictionary;

    @Resource
    private IFinancialCoreDataService financialCoreDataService;

    @Resource
    private ReceiveAdjustDetailService receiveAdjustDetailService;

    @Resource
    private ReceiveAdjustDetailMapper receiveAdjustDetailMapper;

    @Override
    public Page<QueryReceiveAdjustResponse> queryPage(QueryReceiveAdjustRequest queryReceiveAdjustRequest) {
        Page<ReceiveAdjustPo> page = new Page<>(queryReceiveAdjustRequest.getPageNum(),
                queryReceiveAdjustRequest.getPageSize());
        Page<ReceiveAdjustPo> pageSource = page(page, setQueryCondition(queryReceiveAdjustRequest));

        Map<String, String> statusCSSMap = dataDictionary.queryCssTag(DictionaryConstant.ADJUST_STATUS);
        Map<String, String> statusLabel = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_STATUS);
        Map<String, String> reasonMap = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON);
        Map<String, String> reasonOne = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON_OWN);
        Map<String, String> reasonSecond = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON_CUSTOMER);
        reasonMap.putAll(reasonOne);
        reasonMap.putAll(reasonSecond);
        List<QueryReceiveAdjustResponse> list = new ArrayList<>();
        pageSource.getRecords().forEach(adjust -> {
            QueryReceiveAdjustResponse ras = new QueryReceiveAdjustResponse();
            BeanUtil.copyProperties(adjust, ras);
            ras.setStatusCssTag(statusCSSMap.get(adjust.getStatus().toString()));
            ras.setStatusLabel(statusLabel.get(adjust.getStatus().toString()));
            List<String> adjustReasonLabel = new ArrayList<>();
            JSONArray array = new JSONArray(adjust.getAdjustReason());
            for (Object json : array) {
                adjustReasonLabel.add(reasonMap.get(json.toString()));
            }
            Gson gson = new Gson();
            ras.setAdjustReasonLabel(gson.toJson(adjustReasonLabel));
            list.add(ras);
        });
        Page<QueryReceiveAdjustResponse> pageTarget = new Page<>();
        pageTarget.setRecords(list);
        pageTarget.setTotal(pageSource.getTotal());
        pageTarget.setSize(pageSource.getSize());
        pageTarget.setCurrent(pageSource.getCurrent());
        return pageTarget;
    }

    @Override
    public QueryReceiveAdjustResponse query(QueryReceiveAdjustRequest request) {
        QueryReceiveAdjustResponse res = new QueryReceiveAdjustResponse();
        if (request.getAdjustId() == null && StringUtils.isBlank(request.getAdjustNo())) {
            return res;
        }
        LambdaQueryWrapper<ReceiveAdjustPo> query = Wrappers.lambdaQuery();
        query.eq(request.getAdjustId() != null, ReceiveAdjustPo::getAdjustId, request.getAdjustId());
        query.eq(StringUtils.isNotBlank(request.getAdjustNo()), ReceiveAdjustPo::getAdjustNo, request.getAdjustNo());
        ReceiveAdjustPo receiveAdjust = receiveAdjustMapper.selectOne(query);
        Map<String, String> reasonMap = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON);
        Map<String, String> reasonOne = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON_OWN);
        Map<String, String> reasonSecond = dataDictionary.queryNodeLabel(DictionaryConstant.ADJUST_REASON_CUSTOMER);
        reasonMap.putAll(reasonOne);
        reasonMap.putAll(reasonSecond);
        if (receiveAdjust != null) {
            BeanUtil.copyProperties(receiveAdjust, res);
            res.setReceiveAdjustDetails(receiveAdjustDetailService.queryReceiveAdjustDetails(receiveAdjust.getAdjustId()));
            List<String> adjustReasonLabel = new ArrayList<>();
            JSONArray array = new JSONArray(receiveAdjust.getAdjustReason());
            for (Object json : array) {
                adjustReasonLabel.add(reasonMap.get(json.toString()));
            }
            Gson gson = new Gson();
            res.setAdjustReasonLabel(gson.toJson(adjustReasonLabel));
        }
        return res;
    }

    @Override
    @Transactional
    public SaveReceiveAdjustResponse save(SaveReceiveAdjustRequest saveReceiveAdjustRequest) {
        ReceiveAdjustPo ra = new ReceiveAdjustPo();
        ra.setStatus(ReceiveAdjustEnum.Status.TO_SUBMIT.getState());
        SaveReceiveAdjustResponse response = this.saveOrUpdateReceiveAdjust(ra, saveReceiveAdjustRequest);
        receiveAdjustDetailService.saveOrUpdate(ra, saveReceiveAdjustRequest);
        return response;
    }

    @Override
    @Transactional
    public SaveReceiveAdjustResponse submit(SaveReceiveAdjustRequest saveReceiveAdjustRequest) {
        ReceiveAdjustPo ra = new ReceiveAdjustPo();
        ra.setStatus(ReceiveAdjustEnum.Status.FINISH.getState());
        ra.setSubmitTime(LocalDateTime.now());
        ra.setFinishTime(LocalDateTime.now());
        SaveReceiveAdjustResponse response = this.saveOrUpdateReceiveAdjust(ra, saveReceiveAdjustRequest);
        receiveAdjustDetailService.saveOrUpdate(ra, saveReceiveAdjustRequest);
        // 对账
        rentalAdjustmentProfitLoss(ra, saveReceiveAdjustRequest.getReceiveAdjustDetails());
        return response;
    }

    @Override
    @Transactional
    public Boolean delete(Long adjustId) {
        checkReceiveAdjust(adjustId);
        deleteReceiveAdjust(adjustId);
        receiveAdjustDetailService.delete(adjustId);
        return true;
    }

    @Override
    public QueryAdjustRelatedContractResponse queryAdjustRelatedContract(Long contractId) {
        int count = count(Wrappers.lambdaQuery(ReceiveAdjustPo.class).eq(ReceiveAdjustPo::getContractId, contractId)
                .eq(ReceiveAdjustPo::getStatus, ReceiveAdjustEnum.Status.FINISH.getState())
                .eq(ReceiveAdjustPo::getIsDeleted, false));
        BigDecimal contractReceiveTotalAmount = queryContractReceiveTotalAmount(contractId);
        QueryAdjustRelatedContractResponse res = new QueryAdjustRelatedContractResponse();
        res.setHistoryAdjust(count > 0);
        res.setContractReceiveTotalAmount(contractReceiveTotalAmount);
        // T-1天
        res.setEndTime(LocalDate.now().minusDays(1));
        return res;
    }

    @Override
    public boolean sync(SyncReceiveAdjustRequest request) {
        LambdaQueryWrapper<ReceiveAdjustPo> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(ReceiveAdjustPo::getIsDeleted, false)
                .eq(ReceiveAdjustPo::getStatus, ReceiveAdjustEnum.Status.FINISH.getState())
                .eq(request.getAdjustId() != null, ReceiveAdjustPo::getAdjustId, request.getAdjustId())
                .eq(request.getContractId() != null, ReceiveAdjustPo::getContractId, request.getContractId())
                .eq(request.getCustomerId() != null, ReceiveAdjustPo::getCustomerId, request.getCustomerId())
                .eq(request.getSignatoryId() != null, ReceiveAdjustPo::getSignatoryId, request.getSignatoryId())
                .eq(StringUtils.isNotBlank(request.getAdjustNo()), ReceiveAdjustPo::getAdjustNo,
                        request.getAdjustNo())
                .eq(StringUtils.isNotBlank(request.getContractNo()), ReceiveAdjustPo::getContractNo,
                        request.getContractNo());
        List<ReceiveAdjustPo> list = list(lambdaQuery);
        list.forEach(adjust -> {
            List<ReceiveAdjustDetailPo> adjustDetails =
                    receiveAdjustDetailMapper.selectList(Wrappers.lambdaQuery(ReceiveAdjustDetailPo.class)
                            .eq(ReceiveAdjustDetailPo::getAdjustId, adjust.getAdjustId())
                            .eq(ReceiveAdjustDetailPo::getIsDeleted, false));
            rentalAdjustmentProfitLoss(adjust, adjustDetails);
        });
        return true;
    }

    /**
     * 保存/更新应收调整信息
     *
     * @param ra      应收调整信息
     * @param request 应收调整请求
     */
    private SaveReceiveAdjustResponse saveOrUpdateReceiveAdjust(ReceiveAdjustPo ra, SaveReceiveAdjustRequest request) {
        BeanUtil.copyProperties(request, ra);
        if (request.getAdjustId() != null) {
            ReceiveAdjustPo receiveAdjust = queryReceiveAdjustPo(request.getAdjustId());
            ra.setAdjustNo(receiveAdjust.getAdjustNo());
            UserContextUtil.initUpdaterInfo(ra);
        } else {
            ra.setAdjustNo(generator.getNextNo(BusinessTypeEnum.RECEIVE_ADJUST));
            UserContextUtil.initCreatorInfo(ra);
        }

        Gson gson = new Gson();
        ra.setAdjustReason(gson.toJson(request.getAdjustReason()));

        // 计算调整金额
        List<ReceiveAdjustDetailPo> details = request.getReceiveAdjustDetails();
        if (!CollectionUtils.isEmpty(details)) {
            BigDecimal adjustTotalAmount = getAdjustTotalAmount(details);
            ra.setAdjustTotalAmount(adjustTotalAmount);
            // 合同应收总金额
            BigDecimal contractReceiveTotalAmount = request.getContractReceiveTotalAmount();
            ra.setAdjustReceiveTotalAmount(contractReceiveTotalAmount.add(adjustTotalAmount));
        }
        saveOrUpdate(ra);

        SaveReceiveAdjustResponse response = new SaveReceiveAdjustResponse();
        response.setAdjustId(ra.getAdjustId());
        response.setAdjustNo(ra.getAdjustNo());
        return response;
    }

    /**
     * 计算调减总额
     *
     * @param details 调减详情
     * @return 调减总额
     */
    private BigDecimal getAdjustTotalAmount(List<ReceiveAdjustDetailPo> details) {
        BigDecimal adjustTotalAmount = new BigDecimal(0);
        for (ReceiveAdjustDetailPo detail : details) {
            if (detail.getAdjustAmount() != null) {
                if (ReceiveAdjustEnum.AdjustWay.ADD.getWay().equals(detail.getAdjustWay())) {
                    adjustTotalAmount = adjustTotalAmount.add(detail.getAdjustAmount());
                }
                if (ReceiveAdjustEnum.AdjustWay.SUBTRACT.getWay().equals(detail.getAdjustWay())) {
                    adjustTotalAmount = adjustTotalAmount.subtract(detail.getAdjustAmount());
                }
            }
        }
        return adjustTotalAmount;
    }

    /**
     * 删除应收调整
     *
     * @param adjustId 应收调整id
     */
    private void deleteReceiveAdjust(Long adjustId) {
        ReceiveAdjustPo ra = new ReceiveAdjustPo();
        UserContextUtil.initUpdaterInfo(ra);
        ra.setIsDeleted(true);
        update(ra, Wrappers.lambdaUpdate(ReceiveAdjustPo.class).eq(ReceiveAdjustPo::getAdjustId, adjustId));
    }


    /**
     * 设置LambdaQueryWrapper
     *
     * @param request 查询索赔单请求
     * @return LambdaQueryWrapper 条件表达式
     */
    private LambdaQueryWrapper<ReceiveAdjustPo> setQueryCondition(QueryReceiveAdjustRequest request) {
        LambdaQueryWrapper<ReceiveAdjustPo> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery.eq(ReceiveAdjustPo::getIsDeleted, false);
        lambdaQuery.like(StringUtils.isNotBlank(request.getAdjustNo()), ReceiveAdjustPo::getAdjustNo,
                request.getAdjustNo());
        lambdaQuery.like(StringUtils.isNotBlank(request.getContractNo()), ReceiveAdjustPo::getContractNo,
                request.getContractNo());
        lambdaQuery.like(StringUtils.isNotBlank(request.getProjectName()), ReceiveAdjustPo::getProjectName,
                request.getProjectName());
        lambdaQuery.ge(request.getSubmitBeginDate() != null, ReceiveAdjustPo::getSubmitTime,
                request.getSubmitBeginDate());
        if (request.getSubmitEndDate() != null) {
            lambdaQuery.le(ReceiveAdjustPo::getSubmitTime, request.getSubmitEndDate().plusDays(1));
        }
        lambdaQuery.ge(request.getFinishBeginDate() != null, ReceiveAdjustPo::getFinishTime,
                request.getFinishBeginDate());
        if (request.getFinishEndDate() != null) {
            lambdaQuery.le(ReceiveAdjustPo::getFinishTime, request.getFinishEndDate().plusDays(1));
        }
        lambdaQuery.orderByAsc(ReceiveAdjustPo::getStatus).orderByDesc(ReceiveAdjustPo::getCreateTime);
        return lambdaQuery;
    }

    /**
     * 查询索赔单
     *
     * @param adjustId 应收调整id
     * @return 索赔信息
     */
    private ReceiveAdjustPo queryReceiveAdjustPo(Long adjustId) {
        ReceiveAdjustPo adjust =
                this.getOne(Wrappers.lambdaQuery(ReceiveAdjustPo.class).eq(ReceiveAdjustPo::getAdjustId, adjustId)
                        .eq(ReceiveAdjustPo::getIsDeleted, false));
        if (adjust == null) {
            throw new HMYRuntimeException("506100000");
        }
        return adjust;
    }

    /**
     * 业务校验
     *
     * @param adjustId 应收调整单id
     */
    private void checkReceiveAdjust(Long adjustId) {
        ReceiveAdjustPo receiveAdjust = queryReceiveAdjustPo(adjustId);
        if (receiveAdjust.getStatus().equals(ReceiveAdjustEnum.Status.FINISH.getState())) {
            throw new HMYRuntimeException("506100001");
        }
    }

    /**
     * 查询合同
     *
     * @param contractId 合同id
     * @return 应收余额合计
     */
    public BigDecimal queryContractReceiveTotalAmount(Long contractId) {
        BigDecimal totalReceivableAmount = null;
        ReceivableInventoryPo inventory =
                receivableInventory.selectOne(Wrappers.lambdaQuery(ReceivableInventoryPo.class).eq(ReceivableInventoryPo::getContractId,
                        contractId).eq(ReceivableInventoryPo::getIsDeleted, false));
        if (inventory != null) {
            totalReceivableAmount = inventory.getTotalReceivableAmount();
        }
        return totalReceivableAmount;
    }

    /**
     * 对账
     *
     * @param receiveAdjust 应收调整数据
     * @param adjustDetails 应收调整详情
     */
    private void rentalAdjustmentProfitLoss(ReceiveAdjustPo receiveAdjust, List<ReceiveAdjustDetailPo> adjustDetails) {
        ReceivableAdjustGenerateRequest request = new ReceivableAdjustGenerateRequest();
        request.setCustomerId(receiveAdjust.getCustomerId());
        request.setContractId(receiveAdjust.getContractId());
        request.setBusinessBillId(receiveAdjust.getAdjustId());
        List<ReceivableAdjustDetailVo> list = new ArrayList<>();
        adjustDetails.forEach(
                d -> {
                    ReceivableAdjustDetailVo receivableAdjustDetailVo = new ReceivableAdjustDetailVo();
                    receivableAdjustDetailVo.setAdjustAmount(d.getAdjustAmount());
                    // 调整类型
                    if (String.valueOf(d.getAdjustType()).equals(ReceivableAdjustTypeEnum.SHIPPING.getValue())) {
                        receivableAdjustDetailVo.setAdjustType(ReceivableAdjustTypeEnum.SHIPPING);
                    } else if (String.valueOf(d.getAdjustType()).equals(ReceivableAdjustTypeEnum.MODIFICATION.getValue())) {
                        receivableAdjustDetailVo.setAdjustType(ReceivableAdjustTypeEnum.MODIFICATION);
                    } else if (String.valueOf(d.getAdjustType()).equals(ReceivableAdjustTypeEnum.RENT.getValue())) {
                        receivableAdjustDetailVo.setAdjustType(ReceivableAdjustTypeEnum.RENT);
                    } else if (String.valueOf(d.getAdjustType()).equals(ReceivableAdjustTypeEnum.CLAIMS.getValue())) {
                        receivableAdjustDetailVo.setAdjustType(ReceivableAdjustTypeEnum.CLAIMS);
                    }
                    // 盈亏类型
                    if (String.valueOf(d.getAdjustWay()).equals(ProfitLossTypeEnum.PROFIT.getValue())) {
                        receivableAdjustDetailVo.setProfitLossType(ProfitLossTypeEnum.PROFIT);
                    } else if (String.valueOf(d.getAdjustWay()).equals(ProfitLossTypeEnum.LOSS.getValue())) {
                        receivableAdjustDetailVo.setProfitLossType(ProfitLossTypeEnum.LOSS);
                    }
                    receivableAdjustDetailVo.setAdjustRemark(d.getAdjustReason());
                    list.add(receivableAdjustDetailVo);
                }
        );
        request.setReceivableAdjustDetailVoList(list);
        LOG.info("financialCoreDataService.rentalAdjustmentProfitLoss parameter: {}", request);
        BaseGenerateProfitLossResponse response = financialCoreDataService.rentalAdjustmentProfitLoss(request);
        LOG.info("financialCoreDataService.rentalAdjustmentProfitLoss end: {}", response);
        if (!response.getResult()) {
            throw new HMYRuntimeException(506100003);
        }
    }

}




